package com.minimalist.lindi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.Assert;
import com.minimalist.common.config.exception.BusinessException;
import com.minimalist.common.config.mybatis.bo.PageResp;
import com.minimalist.common.entity.basic.enums.FileEnum;
import com.minimalist.common.entity.basic.enums.RespEnum;
import com.minimalist.common.entity.basic.vo.file.FileVO;
import com.minimalist.common.entity.lindi.enums.LindiEnterpriseAccountEnum;
import com.minimalist.common.entity.lindi.enums.LindiTradeApplicationEnum;
import com.minimalist.common.entity.lindi.enums.LindiTradeApplicationFileEnum;
import com.minimalist.common.entity.lindi.enums.TradeStatusEnum;
import com.minimalist.common.entity.lindi.po.LindiPriceConfig;
import com.minimalist.common.entity.lindi.po.LindiTradeApplication;
import com.minimalist.common.entity.lindi.vo.enterpriseaccount.LindiEnterpriseAccountVO;
import com.minimalist.common.entity.lindi.vo.tradeapplication.LindiTradeApplicationAgreeVO;
import com.minimalist.common.entity.lindi.vo.tradeapplication.LindiTradeApplicationQueryVO;
import com.minimalist.common.entity.lindi.vo.tradeapplication.LindiTradeApplicationVO;
import com.minimalist.common.entity.lindi.vo.tradeapplicationfile.LindiTradeApplicationFileVO;
import com.minimalist.common.feign.client.basic.FileFeignClient;
import com.minimalist.common.response.R;
import com.minimalist.common.utils.UnqIdUtil;
import com.minimalist.lindi.manager.LindiEnterpriseManager;
import com.minimalist.lindi.manager.LindiTradeApplicationManager;
import com.minimalist.lindi.mapper.LindiTradeApplicationMapper;
import com.minimalist.lindi.service.LindiPriceConfigService;
import com.minimalist.lindi.service.LindiTradeApplicationService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;

/**
 * 林地交易指标申请 服务层实现。
 *
 * @author 11987
 * @since 2025-04-18
 */
@Service
public class LindiTradeApplicationServiceImpl extends
        ServiceImpl<LindiTradeApplicationMapper, LindiTradeApplication>
        implements LindiTradeApplicationService{

    private static final Logger log = LoggerFactory.getLogger(LindiTradeApplicationServiceImpl.class);

    @Autowired
    private LindiTradeApplicationMapper mapper;

    @Autowired
    private LindiEnterpriseManager lindiEnterpriseManager;

    @Autowired
    private LindiTradeApplicationManager lindiTradeApplicationManager;

    @Autowired
    private FileFeignClient fileFeignClient;

    @Autowired
    private LindiPriceConfigService lindiPriceConfigService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addTradeApplication(LindiTradeApplicationVO tradeApplicationVO){
        LindiTradeApplication tradeApplication = BeanUtil.copyProperties(
                tradeApplicationVO, LindiTradeApplication.class);
        tradeApplication.setTradeApplicationId(UnqIdUtil.uniqueId());
        tradeApplication.setTradeStatus(TradeStatusEnum.PENDING_FORESTRY_INITIAL_REVIEW
                .getCode());
        if (tradeApplicationVO.getApplicationTime() == null){
            tradeApplication.setApplicationTime(LocalDateTime.now());
        }

        // 验证面积字段格式
        validateAreaFields(tradeApplication);

        // 计算总面积
        BigDecimal totalArea = BigDecimal.ZERO;
        if (tradeApplication.getReserveArea() != null && !tradeApplication.getReserveArea().trim().isEmpty()){
            totalArea = totalArea.add(parseAreaValue(tradeApplication.getReserveArea(), "储备库指标面积"));
        }
        if (tradeApplication.getPublicForestArea() != null && !tradeApplication.getPublicForestArea().trim().isEmpty()){
            totalArea = totalArea.add(parseAreaValue(tradeApplication.getPublicForestArea(), "公益林面积"));
        }
        tradeApplication.setTotalArea(totalArea.toString());

        // 设置申请理由
        String applicationReason = "  我单位因" + (tradeApplication.getProjectName()
                != null ? tradeApplication.getProjectName() : "______")
                + "项目建设需要，意向向晋农公司购买林地指标" + totalArea
                + "亩，并签订相关合同。我司承诺签约后按期支付相关费用。\n\n申请单位签字（公章）：           日期：  年   月   日";
        tradeApplication.setApplicationReason(applicationReason);

        if (tradeApplicationVO.getMoney() == null
                || tradeApplicationVO.getMoney().isEmpty()){
            // 计算项目总金额
            BigDecimal totalProjectAmount = calculateTotalProjectAmount(tradeApplication);
            if (totalProjectAmount.compareTo(BigDecimal.ZERO) > 0){
                tradeApplication.setMoney(totalProjectAmount.toString());
            }
        } else{
            tradeApplication.setMoney(tradeApplicationVO.getMoney());
        }

        // 保存申请记录和文件
        saveTradeApplicationAndFiles(tradeApplicationVO, tradeApplication);
    }

    /**
     * 验证面积字段格式
     */
    private void validateAreaFields(LindiTradeApplication tradeApplication){
        // 验证储备库指标面积
        if (tradeApplication.getReserveArea() != null && !tradeApplication.getReserveArea().trim().isEmpty()){
            if (isValidNumber(tradeApplication.getReserveArea())){
                throw new BusinessException("储备库指标面积格式错误，请输入有效的数字");
            }
        }

        // 验证公益林面积
        if (tradeApplication.getPublicForestArea() != null && !tradeApplication.getPublicForestArea().trim().isEmpty()){
            if (isValidNumber(tradeApplication.getPublicForestArea())){
                throw new BusinessException("公益林面积格式错误，请输入有效的数字");
            }
        }
    }

    /**
     * 验证字符串是否为有效数字
     */
    private boolean isValidNumber(String value){
        if (value == null || value.trim().isEmpty()){
            return true;
        }
        try{
            new BigDecimal(value.trim());
            return false;
        } catch (NumberFormatException e){
            return true;
        }
    }

    /**
     * 计算项目总金额
     */
    private BigDecimal calculateTotalProjectAmount(LindiTradeApplication tradeApplication){
        BigDecimal totalAmount = BigDecimal.ZERO;
        LocalDateTime now = LocalDateTime.now();

        // 计算储备库指标金额
        BigDecimal reserveAmount = calculateReserveAmount(tradeApplication, now);
        tradeApplication.setReserveAreaMoney(reserveAmount);
        totalAmount = totalAmount.add(reserveAmount);

        // 计算公益林金额
        BigDecimal publicForestAmount = calculatePublicForestAmount(tradeApplication, now);
        tradeApplication.setPublicForestAreaMoney(publicForestAmount);
        totalAmount = totalAmount.add(publicForestAmount);

        // 记录计算结果
        if (totalAmount.compareTo(BigDecimal.ZERO) > 0){
            log.info("项目总金额计算完成: 储备库金额={}, 公益林金额={}, 总金额={}",
                    reserveAmount, publicForestAmount, totalAmount);
        } else{
            log.warn("项目总金额为0，储备库面积={}, 公益林面积={}",
                    tradeApplication.getReserveArea(), tradeApplication.getPublicForestArea());
        }

        return totalAmount;
    }

    /**
     * 计算储备库指标金额
     */
    private BigDecimal calculateReserveAmount(LindiTradeApplication tradeApplication, LocalDateTime now){
        BigDecimal reserveArea = parseAreaValue(tradeApplication.getReserveArea(), "储备库指标面积");
        if (reserveArea.compareTo(BigDecimal.ZERO) <= 0){
            return BigDecimal.ZERO;
        }

        return calculateAmountByPriceConfig("0", reserveArea, "储备库指标", now);
    }

    /**
     * 计算公益林金额
     */
    private BigDecimal calculatePublicForestAmount(LindiTradeApplication tradeApplication, LocalDateTime now){
        BigDecimal publicForestArea = parseAreaValue(tradeApplication.getPublicForestArea(), "公益林面积");
        if (publicForestArea.compareTo(BigDecimal.ZERO) <= 0){
            return BigDecimal.ZERO;
        }

        return calculateAmountByPriceConfig("1", publicForestArea, "公益林", now);
    }

    /**
     * 解析面积值
     */
    private BigDecimal parseAreaValue(String areaStr, String areaType){
        if (areaStr == null || areaStr.trim().isEmpty()){
            return BigDecimal.ZERO;
        }

        try{
            return new BigDecimal(areaStr);
        } catch (NumberFormatException e){
            log.warn("{}格式错误: {}", areaType, areaStr);
            return BigDecimal.ZERO;
        }
    }

    /**
     * 根据价格配置计算金额
     */
    private BigDecimal calculateAmountByPriceConfig(String priceType, BigDecimal area, String areaTypeName, LocalDateTime now){
        try{
            LindiPriceConfig priceConfig = lindiPriceConfigService.getValidPriceConfigByType(priceType, now);
            if (priceConfig == null || priceConfig.getUnitPrice() == null){
                log.warn("未找到{}的有效价格配置", areaTypeName);
                return BigDecimal.ZERO;
            }

            try{
                BigDecimal unitPrice = new BigDecimal(priceConfig.getUnitPrice());
                BigDecimal amount = unitPrice.multiply(area).setScale(2, RoundingMode.HALF_UP);
                log.info("{}金额计算成功: 单价={}, 面积={}, 金额={}", areaTypeName, unitPrice, area, amount);
                return amount;
            } catch (NumberFormatException e){
                log.warn("{}单价格式错误: {}", areaTypeName, priceConfig.getUnitPrice());
                return BigDecimal.ZERO;
            }
        } catch (Exception e){
            log.warn("查询{}价格配置失败: {}", areaTypeName, e.getMessage());
            return BigDecimal.ZERO;
        }
    }

    /**
     * 保存交易申请和文件
     */
    private void saveTradeApplicationAndFiles(LindiTradeApplicationVO tradeApplicationVO, LindiTradeApplication tradeApplication){
        List<Long> otherFiles = tradeApplicationVO.getOtherFiles();
        lindiTradeApplicationManager.insertTradeApplicationFiles(
                tradeApplication.getTradeApplicationId(), otherFiles);
        int insert = mapper.insert(tradeApplication, true);
        Assert.isTrue(insert == 1, () -> new BusinessException(
                LindiTradeApplicationEnum.ErrorMsgEnum.ADD_TRADE_APPLICATION_ERROR
                        .getDesc()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTradeApplicationByTradeApplicationId(Long tradeApplicationId){
        LindiTradeApplication tradeApplication = mapper
                .selectTradeApplicationByTradeApplicationId(tradeApplicationId);
        Assert.notNull(tradeApplication,
                () -> new BusinessException(LindiTradeApplicationEnum.ErrorMsgEnum
                        .NONENTITY_TRADE_APPLICATION.getDesc()));

        // 尝试删除文件，但不影响记录删除
        try{
            List<LindiTradeApplicationFileVO> tradeApplicationFiles = lindiTradeApplicationManager
                    .getTradeApplicationFilesByApplicationId(tradeApplicationId);
            List<Long> fileIds = new ArrayList<>(tradeApplicationFiles.stream().map(
                    LindiTradeApplicationFileVO::getTradeApplicationFileId).toList());
            fileIds.add(tradeApplication.getRedlineMapId());
            R<Void> result = fileFeignClient.deleteBatchFiles(fileIds);
            if (!Objects.equals(result.getCode(), RespEnum.SUCCESS.getCode())){
                log.warn("删除交易申请文件失败，但继续删除记录。交易申请ID: {}, 错误信息: {}",
                        tradeApplicationId, result.getMessage());
            }
        } catch (Exception e){
            log.error("删除交易申请文件时发生异常，但继续删除记录。交易申请ID: {}", tradeApplicationId, e);
        }

        // 删除数据库记录
        lindiTradeApplicationManager.deleteTradeApplicationFilesByTradeApplicationId(
                tradeApplicationId);
        lindiTradeApplicationManager
                .deleteTradeApplicationOperatorsByTradeApplicationId(tradeApplicationId);
        int delete = mapper.delete(tradeApplication);
        Assert.isTrue(delete == 1, () -> new BusinessException(
                LindiTradeApplicationEnum.ErrorMsgEnum.DELETE_TRADE_APPLICATION_ERROR
                        .getDesc()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTradeApplication(LindiTradeApplicationVO tradeApplicationVO){
        log.info("更新交易申请，tradeApplicationVO: {}", tradeApplicationVO);
        LindiTradeApplication tradeApplication = mapper
                .selectTradeApplicationByTradeApplicationId(tradeApplicationVO.getTradeApplicationId());
        Assert.notNull(tradeApplication,
                () -> new BusinessException(LindiTradeApplicationEnum.ErrorMsgEnum
                        .NONENTITY_TRADE_APPLICATION.getDesc()));
        BeanUtil.copyProperties(tradeApplicationVO, tradeApplication
                , CopyOptions.create().ignoreNullValue());

        // 计算总面积
        BigDecimal totalArea = BigDecimal.ZERO;
        if (tradeApplication.getReserveArea() != null && !tradeApplication.getReserveArea().trim().isEmpty()){
            totalArea = totalArea.add(parseAreaValue(tradeApplication.getReserveArea(), "储备库指标面积"));
        }
        if (tradeApplication.getPublicForestArea() != null && !tradeApplication.getPublicForestArea().trim().isEmpty()){
            totalArea = totalArea.add(parseAreaValue(tradeApplication.getPublicForestArea(), "公益林面积"));
        }
        tradeApplication.setTotalArea(totalArea.toString());

        // 计算各项金额
        LocalDateTime now = LocalDateTime.now();
        BigDecimal reserveAmount = calculateReserveAmount(tradeApplication, now);
        BigDecimal publicForestAmount = calculatePublicForestAmount(tradeApplication, now);
        tradeApplication.setReserveAreaMoney(reserveAmount);
        tradeApplication.setPublicForestAreaMoney(publicForestAmount);

        // 计算总金额并设置
        BigDecimal totalAmount = reserveAmount.add(publicForestAmount);
        tradeApplication.setMoney(totalAmount.toString());

        // 设置申请理由
        String applicationReason = "我单位因" + (tradeApplication.getProjectName() != null ? tradeApplication.getProjectName() : "______") + "项目建设需要，意向向晋农公司购买林地指标" + totalArea + "亩，并签订相关合同。我司承诺签约后按期支付相关费用。\n\n申请单位签字（公章）：                日期：  年   月   日";
        tradeApplication.setApplicationReason(applicationReason);

        lindiTradeApplicationManager.deleteTradeApplicationFilesByTradeApplicationId(
                tradeApplicationVO.getTradeApplicationId());
        int update = mapper.update(tradeApplication);
        lindiTradeApplicationManager.insertTradeApplicationFiles(
                tradeApplicationVO.getTradeApplicationId()
                , tradeApplicationVO.getOtherFiles());
        Assert.isTrue(update == 1, () -> new BusinessException(
                LindiTradeApplicationEnum.ErrorMsgEnum.UPDATE_TRADE_APPLICATION_ERROR
                        .getDesc()));
    }

    @Override
    public PageResp<LindiTradeApplicationVO> getPageTradeApplicationList(
            LindiTradeApplicationQueryVO queryVO){
        System.out.println("queryVO: " + queryVO);
        // 分页查询
        Page<LindiTradeApplication> applicationPage = mapper
                .selectPageTradeApplicationList(queryVO);
        // 数据转换
        List<LindiTradeApplicationVO> applicationVOList = BeanUtil.copyToList(
                applicationPage.getRecords(), LindiTradeApplicationVO.class);
        return new PageResp<>(applicationVOList, applicationPage.getTotalRow());
    }

    @Override
    public LindiTradeApplicationVO getTradeApplicationByTradeApplicationId(
            Long tradeApplicationId){
        LindiTradeApplication tradeApplication = mapper
                .selectTradeApplicationByTradeApplicationId(tradeApplicationId);
        Assert.notNull(tradeApplication,
                () -> new BusinessException(LindiTradeApplicationEnum.ErrorMsgEnum
                        .NONENTITY_TRADE_APPLICATION.getDesc()));
        LindiTradeApplicationVO lindiTradeApplicationVO = BeanUtil.copyProperties(
                tradeApplication, LindiTradeApplicationVO.class);
        LindiEnterpriseAccountVO enterpriseAccountVO = lindiEnterpriseManager
                .getLindiEnterpriseAccountByEnterpriseAccountId(
                        lindiTradeApplicationVO.getEnterpriseAccountId());
        Assert.notNull(enterpriseAccountVO,
                () -> new BusinessException(LindiEnterpriseAccountEnum.ErrorMsgEnum
                        .ENTERPRISE_ACCOUNT_NOT_EXIST.getDesc()));
        lindiTradeApplicationVO.setEnterpriseAccount(enterpriseAccountVO);
        R<FileVO> redlineMap = fileFeignClient.getFileByFileId(
                lindiTradeApplicationVO.getRedlineMapId());
        lindiTradeApplicationVO.setRedlineMapFile(redlineMap.getData());
        List<LindiTradeApplicationFileVO> lindiTradeApplicationFileList =
                lindiTradeApplicationManager.getTradeApplicationFilesByApplicationId(
                        tradeApplicationId);
        R<List<FileVO>> files = fileFeignClient.getFileListByFileIds(
                lindiTradeApplicationFileList.stream().map(
                                LindiTradeApplicationFileVO::getTradeApplicationFileId)
                        .toList());
        Assert.isTrue(Objects.equals(files.getCode(), RespEnum.SUCCESS.getCode()));
        lindiTradeApplicationVO.setOtherFilesVO(files.getData());
        log.info("获取交易详情成功：{}", lindiTradeApplicationVO);
        return lindiTradeApplicationVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agreeTradeApplication(LindiTradeApplicationAgreeVO agreeVO){
        LindiTradeApplication tradeApplication = mapper
                .selectTradeApplicationByTradeApplicationId(
                        agreeVO.getTradeApplicationId());
        Assert.notNull(tradeApplication,
                () -> new BusinessException(LindiTradeApplicationEnum.ErrorMsgEnum
                        .NONENTITY_TRADE_APPLICATION.getDesc()));
        if (Objects.equals(agreeVO.getAgree(), LindiTradeApplicationEnum.AgreeEnum
                .AGREE.getCode())){
            tradeApplication.setTradeStatus(TradeStatusEnum
                    .PENDING_TRADE_APPROVAL.getCode());
            // 生成交易编号：JY + 年月日 + 6位随机数
            String tradeCode = generateTradeCode();
            tradeApplication.setTradeCode(tradeCode);
            LindiTradeApplicationFileVO lindiTradeApplicationFileVO
                    = getLindiTradeApplicationFileVO(tradeCode);
            lindiTradeApplicationFileVO.setTradeApplicationId(
                    tradeApplication.getTradeApplicationId());
            lindiTradeApplicationFileVO.setFileType(FileEnum.FileSource
                    .LINDI_TRADE_CONFIRMATION_LETTER.getCode());
            lindiTradeApplicationFileVO.setFileTypeDesc(LindiTradeApplicationFileEnum
                    .FileTypeEnum.CONFIRMATION_LETTER.getDesc());
            lindiTradeApplicationFileVO.setCreateId(agreeVO.getUpdateId());
            lindiTradeApplicationFileVO.setUpdateId(agreeVO.getUpdateId());
            LocalDateTime now = LocalDateTime.now();
            lindiTradeApplicationFileVO.setCreateTime(now);
            lindiTradeApplicationFileVO.setUpdateTime(now);
            lindiTradeApplicationFileVO.setTenantId(agreeVO.getTenantId());
            LindiTradeApplicationVO lindiTradeApplicationVO = BeanUtil.copyProperties(
                    tradeApplication, LindiTradeApplicationVO.class);
            LindiTradeApplicationFileVO tradeApplicationFile =
                    lindiTradeApplicationManager.createTradeApplicationFile(
                            lindiTradeApplicationFileVO, lindiTradeApplicationVO);
            Assert.notNull(tradeApplicationFile,
                    () -> new BusinessException(LindiTradeApplicationEnum.ErrorMsgEnum
                            .UPDATE_TRADE_APPLICATION_ERROR.getDesc()));
        } else{
            tradeApplication.setTradeStatus(TradeStatusEnum.REJECTED.getCode());
            tradeApplication.setRefuseReason(agreeVO.getRefuseReason());
        }
        tradeApplication.setUpdateId(agreeVO.getUpdateId());
        tradeApplication.setUpdateTime(agreeVO.getUpdateTime());
        mapper.updateTradeApplicationByTradeApplicationId(tradeApplication);
        lindiTradeApplicationManager.addAgreeTradeApplicationOperator(agreeVO);
    }

    @NotNull
    private static LindiTradeApplicationFileVO getLindiTradeApplicationFileVO(
            String tradeCode){
        LindiTradeApplicationFileVO lindiTradeApplicationFileVO =
                new LindiTradeApplicationFileVO();
        lindiTradeApplicationFileVO.setFileName(tradeCode + "_" +
                LindiTradeApplicationFileEnum
                        .FileTypeEnum.CONFIRMATION_LETTER.getDesc());
        lindiTradeApplicationFileVO.setFileType(LindiTradeApplicationFileEnum
                .FileTypeEnum.CONFIRMATION_LETTER.getCode());
        lindiTradeApplicationFileVO.setFileTypeDesc(LindiTradeApplicationFileEnum
                .FileTypeEnum.CONFIRMATION_LETTER.getDesc());
        return lindiTradeApplicationFileVO;
    }

    /**
     * 生成交易编号
     * 格式：JY + 年月日 + 6位随机数
     *
     * @return 交易编号
     */
    private String generateTradeCode(){
        // 获取当前日期并格式化为yyyyMMdd
        LocalDate today = LocalDate.now();
        String dateStr = today.format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        // 生成6位随机数
        Random random = new Random();
        // 确保是6位数
        int randomNum = random.nextInt(900000) + 100000;

        // 组合成交易编号
        return "JY" + dateStr + randomNum;
    }

    @Override
    public PageResp<LindiTradeApplicationVO> getCompletedTradeApplicationList(
            LindiTradeApplicationQueryVO queryVO){
        log.info("分页查询已完成的交易记录，查询参数：{}", queryVO);

        try{
            // 设置查询条件：只查询已完成的交易记录
            queryVO.setTradeStatus(TradeStatusEnum.FORESTRY_APPROVAL_PASSED_TRADE_COMPLETED.getCode());

            // 调用现有的分页查询方法
            PageResp<LindiTradeApplicationVO> pageResp = getPageTradeApplicationList(queryVO);

            log.info("分页查询已完成的交易记录成功，共查询到{}条记录", pageResp.getTotal());
            return pageResp;
        } catch (Exception e){
            log.error("分页查询已完成的交易记录失败", e);
            throw new BusinessException(RespEnum.FAILED.getCode(), "查询已完成的交易记录失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveTradeApplication(LindiTradeApplicationAgreeVO agreeVO){
        log.info("审批交易申请，参数：{}", agreeVO);

        // 调用manager层的审批方法
        lindiTradeApplicationManager.approveTradeApplication(agreeVO);

        log.info("审批交易申请成功，交易申请ID：{}", agreeVO.getTradeApplicationId());
    }
}
